#ifndef _BASIS_VECTOR3_H_
#define _BASIS_VECTOR3_H_

#include <iostream>
#include <cmath>
using namespace std;

#define PI		3.141592653589f
#define INV_PI	0.318309886184f

class Vector3
{
public:
	Vector3();
	Vector3(float e0, float e1, float e2);
	Vector3(const Vector3& v);

	float x() const;
	float y() const;
	float z() const;

	const Vector3& operator+() const;
	Vector3 operator-() const;
	float operator[](int i) const;
	float& operator[](int i);

	float length() const;
	float squaredLength() const;

	void makeUnitVector();

	void setX(float _x);
	void setY(float _y);
	void setZ(float _z);

	float minComponent() const;
	float maxComponent() const;
	float minAbsComponent() const;
	float maxAbsComponent() const;
	int indexOfMinComponent() const;
	int indexOfMinAbsComponent() const;
	int indexOfMaxComponent() const;
	int indexOfMaxAbsComponent() const;

	friend bool operator==(const Vector3& v1, const Vector3& v2);
	friend bool operator!=(const Vector3& v1, const Vector3& v2);

	friend istream &operator>>(istream &is, Vector3 &t);
	friend ostream &operator<<(ostream &os, const Vector3 &t);

	friend Vector3 operator+(const Vector3& v1, const Vector3& v2);
	friend Vector3 operator-(const Vector3& v1, const Vector3& v2);
	friend Vector3 operator/(const Vector3& vec, float scalar);
	friend Vector3 operator*(const Vector3& vec, float scalar);
	friend Vector3 operator*(float scalar, const Vector3& vec);

	Vector3& operator=(const Vector3& v2);
	Vector3& operator+=(const Vector3& v2);
	Vector3& operator-=(const Vector3& v2);
	Vector3& operator*=(const float t);
	Vector3& operator/=(const float t);

	friend Vector3 unitVector(const Vector3& v);
	friend Vector3 minVec(const Vector3& v1, const Vector3& v2);
	friend Vector3 maxVec(const Vector3& v1, const Vector3& v2);
	friend Vector3 cross(const Vector3& v1, const Vector3& v2);
	friend float dot(const Vector3& v1, const Vector3& v2);
	friend float tripleProduct(const Vector3& v1, const Vector3& v2, const Vector3& v3);

protected:

	float e[3];
};

inline Vector3::Vector3(float e0, float e1, float e2)
{
	e[0] = e0;
	e[1] = e1;
	e[2] = e2;
}

inline const Vector3& Vector3::operator+() const
{
	return *this;
}

inline Vector3 Vector3::operator-() const
{
	return Vector3(-e[0], -e[1], -e[2]);
}

inline float Vector3::length() const
{
	float sum = 0.0f;
	for (int i = 0; i < 3; i++)
		sum += e[i] * e[i];
	return sqrt(sum);
}

inline float Vector3::squaredLength() const
{
	float sum = 0.0f;
	for (int i = 0; i < 3; i++)
		sum += e[i] * e[i];
	return sum;
}

inline void Vector3::makeUnitVector()
{
	*this = (*this) / length();
}

inline float Vector3::minComponent() const
{
	if (e[0] < e[1])
		return (e[0] < e[2]) ? e[0] : e[2];
	else
		return (e[1] < e[2]) ? e[1] : e[2];
}

inline float Vector3::maxComponent() const
{
	if (e[0] > e[1])
		return (e[0] > e[2]) ? e[0] : e[2];
	else
		return (e[1] > e[2]) ? e[1] : e[2];
}

inline float Vector3::minAbsComponent() const
{
	float temp = fabs(e[0]);
	if (temp > fabs(e[1])) temp = fabs(e[1]);
	if (temp > fabs(e[2])) temp = fabs(e[2]);
	return temp;
}

inline float Vector3::maxAbsComponent() const
{
	float temp = fabs(e[0]);
	if (temp < fabs(e[1])) temp = fabs(e[1]);
	if (temp < fabs(e[2])) temp = fabs(e[2]);

	return temp;
}

inline int Vector3::indexOfMaxComponent() const
{
	int index = 0;
	float temp = e[0];
	if (temp < e[1]) 
	{
		temp = e[1];
		index = 1;
	}
	if (temp < e[2])
	{
		index = 2;
	}
	return index;
}

inline int Vector3::indexOfMinComponent() const
{
	int index = 0;
	float temp = e[0];
	if (temp > e[1])
	{
		temp = e[1];
		index = 1;
	}
	if (temp > e[2])
	{
		index = 2;
	}
	return index;
}

inline int Vector3::indexOfMaxAbsComponent() const
{
	int index = 0;
	float temp = fabs(e[0]);
	if (temp < fabs(e[1]))
	{
		temp = fabs(e[1]);
		index = 1;
	}
	if (temp < fabs(e[2]))
	{
		index = 2;
	}
	return index;
}

inline int Vector3::indexOfMinAbsComponent() const
{
	int index = 0;
	float temp = fabs(e[0]);
	if (temp > fabs(e[1]))
	{
		temp = fabs(e[1]);
		index = 1;
	}
	if (temp > fabs(e[2]))
	{
		index = 2;
	}
	return index;
}

inline bool operator==(const Vector3& v1, const Vector3& v2)
{
	return (v1.e[0] == v2.e[0]) 
		&& (v1.e[1] == v2.e[1])
		&& (v1.e[2] == v2.e[2]);
}

inline bool operator!=(const Vector3& v1, const Vector3& v2)
{
	return !(v1 == v2);
}

inline Vector3 operator+(const Vector3& v1, const Vector3& v2)
{
	return Vector3(v1.e[0] + v2.e[0], v1.e[1] + v2.e[1], v1.e[2] + v2.e[2]);
}

inline Vector3 operator-(const Vector3& v1, const Vector3& v2)
{
	return Vector3(v1.e[0] - v2.e[0], v1.e[1] - v2.e[1], v1.e[2] - v2.e[2]);
}

inline Vector3 operator/(const Vector3& vec, float scalar)
{
	return Vector3(vec.e[0] / scalar, vec.e[1] / scalar, vec.e[2] / scalar);
}

inline Vector3 operator*(const Vector3& vec, float scalar)
{
	return Vector3(vec.e[0] * scalar, vec.e[1] * scalar, vec.e[2] * scalar);
}

inline Vector3 operator*(float scalar, const Vector3& vec)
{
	return Vector3(vec.e[0] * scalar, vec.e[1] * scalar, vec.e[2] * scalar);
}

inline Vector3& Vector3::operator=(const Vector3& v2)
{
	e[0] = v2.e[0];
	e[1] = v2.e[1];
	e[2] = v2.e[2];
	return *this;
}

inline Vector3& Vector3::operator+=(const Vector3& v2)
{
	*this = *this + v2;
	return *this;
}

inline Vector3& Vector3::operator-=(const Vector3& v2)
{
	*this = *this - v2;
	return *this;
}

inline Vector3& Vector3::operator*=(const float t)
{
	*this = *this * t;
	return *this;
}

inline Vector3& Vector3::operator/=(const float t)
{
	*this = *this / t;
	return *this;
}

inline Vector3 unitVector(const Vector3& v)
{
	return v / v.length();
}


//	bit-wise min
inline Vector3 minVec(const Vector3& v1, const Vector3& v2)
{
	Vector3 temp(v1);
	if (v1.e[0] > v2.e[0]) temp.setX(v2.e[0]);
	if (v1.e[1] > v2.e[1]) temp.setY(v2.e[1]);
	if (v1.e[2] > v2.e[2]) temp.setZ(v2.e[2]);
	return temp;
}

//	bit-wise max
inline Vector3 maxVec(const Vector3& v1, const Vector3& v2)
{
	Vector3 temp(v1);
	if (v1.e[0] < v2.e[0]) temp.setX(v2.e[0]);
	if (v1.e[1] < v2.e[1]) temp.setY(v2.e[1]);
	if (v1.e[2] < v2.e[2]) temp.setZ(v2.e[2]);
	return temp;
}

inline Vector3 cross(const Vector3& v1, const Vector3& v2)
{
	Vector3 temp;
	temp.setX(v1.e[1] * v2.e[2] - v1.e[2] * v2.e[1]);
	temp.setY(v1.e[2] * v2.e[0] - v1.e[0] * v2.e[2]);
	temp.setZ(v1.e[0] * v2.e[1] - v1.e[1] * v2.e[0]);

	return temp;
}

inline float dot(const Vector3 &v1, const Vector3 &v2)
{
	return v1.e[0] * v2.e[0] + v1.e[1] * v2.e[1] + v1.e[2] * v2.e[2];
}




#endif